package com.example.service;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.service.IoService;
import org.apache.mina.core.service.IoServiceListener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.FilterEvent;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;

import java.net.InetSocketAddress;

@Service
public class ClientServiceImpl implements ClientService, IoServiceListener, InitializingBean, CommandLineRunner, DisposableBean {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${mina.url}")
    private String url;

    @Value("${mina.port}")
    private int port;

    @Value("${mina.interval}")
    private int interval;

    private NioSocketConnector acceptor;

    private IoHandler ioHandler;

    private int count = 0;

    public IoHandler getIoHandler() {
        return ioHandler;
    }

    public IoSession getIoSession() {
        return ((ClientHandler) this.ioHandler).getIoSession();
    }

    @Override
    public void connect() throws Exception {
        ConnectFuture connectFuture = this.acceptor.connect(new InetSocketAddress(url, port)).awaitUninterruptibly();
        if (connectFuture.isConnected()) {
            count = 0;
            logger.warn("服务端:" + url + ":" + port + " 连接成功");
        } else {
            count++;
            logger.warn("服务端:" + url + ":" + port + " 连接失败, 将于 " + (count * interval) + " 秒后重试 ...");
            Thread.sleep(count * interval * 1000);
            this.connect();
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.acceptor = new NioSocketConnector();
        this.acceptor.setConnectTimeoutCheckInterval(5000);
        this.acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
        this.acceptor.getSessionConfig().setReadBufferSize(1024 * 2);
        this.acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        this.ioHandler = new ClientHandler();
        this.acceptor.setHandler(this.ioHandler);
        this.acceptor.addListener(this);
    }

    @Override
    public void destroy() throws Exception {
        this.acceptor.dispose();
    }

    @Override
    public void run(String... args) throws Exception {
        this.connect();
    }

    @Override
    public void serviceActivated(IoService service) throws Exception {

    }

    @Override
    public void serviceIdle(IoService service, IdleStatus idleStatus) throws Exception {
    }

    @Override
    public void serviceDeactivated(IoService service) throws Exception {
        if (!service.isDisposed()) {
            logger.warn("客户端检测: 服务端" + url + ":" + port + "已经掉线，即将重连 ...");
            this.connect();
        }
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {

    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
    }

    @Override
    public void sessionDestroyed(IoSession session) throws Exception {
    }

    class ClientHandler implements IoHandler {

        private IoSession ioSession;

        @Override
        public void sessionCreated(IoSession ioSession) throws Exception {
        }

        @Override
        public void sessionOpened(IoSession ioSession) throws Exception {
            this.ioSession = ioSession;
        }

        @Override
        public void sessionClosed(IoSession ioSession) throws Exception {
            this.ioSession = null;
        }

        @Override
        public void sessionIdle(IoSession ioSession, IdleStatus idleStatus) throws Exception {
        }

        @Override
        public void exceptionCaught(IoSession ioSession, Throwable throwable) throws Exception {
            logger.warn("client exceptionCaught exceptionCaught ...");
        }

        @Override
        public void messageReceived(IoSession ioSession, Object o) throws Exception {
            logger.warn("client 接收消息: " + o);
        }

        @Override
        public void messageSent(IoSession ioSession, Object o) throws Exception {
            logger.warn("client 发送消息完毕: " + o);
        }

        @Override
        public void inputClosed(IoSession ioSession) throws Exception {
        }

        @Override
        public void event(IoSession ioSession, FilterEvent filterEvent) throws Exception {
        }

        public IoSession getIoSession() {
            return ioSession;
        }
    }
}
